#include "include/tss.h"
#include "include/string.h"
#include "include/sched.h"
#include "include/segment.h"
#include "include/print.h"

struct tss tss; // Using kernel data segments

void tss_init() {
  // 1.Init tss(task state segment)
  memset(&tss, 0, sizeof(tss));

  tss.rsp0 = current->kstack; // [Kernel privilege level 0] pointing to the bottom of the current program kernel stack

  // IO_MAP permission settings
  tss.io_bitmap_offset = __builtin_offsetof(struct tss, io_bitmap); // The relative offset of the field io_bitmap in the tss structure
  tss.io_bitmap[IO_BITMAP_BYTES] = 0xff; // Set all 1 in the last 1 byte
  // membitset(tss.io_bitmap, 0x3f8, 1); // set 0x3f8 bit to 1 (default 0): Prohibit program access to port 0x3f8 (output)
  // membitset(tss.io_bitmap, 0x3f9, 1); // set 0x3f9 bit to 1
  // print(0x3f8);
  // print(membitget(tss.io_bitmap, 0x3f7)); // 0 => 0x00000000
  // print(membitget(tss.io_bitmap, 0x3f8)); // 1 => 0x00000001
  // print(membitget(tss.io_bitmap, 0x3f9)); // 2 => 0x00000010
  // print(membitget(tss.io_bitmap, 0x3fa)); // 0 => 0x00000000

  membitset(tss.io_bitmap, 0x3f8, 0); // set 0x3f8 bit to 0 (default 0): Allow program access to port 0x3f8 (output)
  // print(0x3f8);
  // print(membitget(tss.io_bitmap, 0x3f8));

  // 2.Write tss into gdt as system segment descriptor
  struct tss_desc* desc = (struct tss_desc*)&gdt[GDT_TSS_ENTRY]; // pointer to 6th item of gdt
  memset(desc, 0, sizeof(struct tss_desc)); // init 0
  desc->limit0 = sizeof(tss) & 0xffff; // tss segment length low 16-bits
  desc->base0 = (unsigned long)(&tss) & 0xffff; // tss address low 16-bits
  desc->base1 = ((unsigned long)(&tss) >> 16) & 0xff; // tss address middle 8-bits
  desc->type = 0x9; // tss segment type 1001
  desc->p = 1; // existence bit
  desc->limit1 = (sizeof(tss) >> 16) & 0xf; // tss segment length high 4-bits
  desc->base2 = ((unsigned long)(&tss) >> 24) & 0xff; // tss address middle 8-bits
  desc->base3 = (unsigned long)(&tss) >> 32; // tss address high 32-bits

  // The instruction `ltr` loads the TR task register and sets the segment selector to 0x30
  __asm__("ltr %w0" : : "r"(GDT_TSS_ENTRY << 3)); // 0x6 << 3 = 00110000 = 0x30
}